<template>
    <div class="body-inner">
        <h1 class="body-title">
            <i :class="$bodyTitle.icon"></i>
            <span>{{ $bodyTitle.description }}</span>
        </h1>
        <div class="line10"></div>
        <div class="body-main">
            <div class="btns" ref="btns">
                <div class="content">
                    <div class="btn" @click="compress">
                        <i class="icon appfont app-compression"></i>
                        <span>压缩</span>
                    </div>
                    <div class="btn" @click="format">
                        <i class="icon appfont app-unfold"></i>
                        <span>格式化</span>
                    </div>
                    <div class="btn" @click="clear">
                        <i class="icon appfont app-clear"></i>
                        <span>清空</span>
                    </div>
                </div>
                <div class="editor">
                    <div class="btn" :class="{active:editor.readonly}" @click="setReadonly">
                        <i class="icon appfont app-readonly"></i>
                        <span>只读</span>
                    </div>
                    <div class="btn btn-indent" @click.stop="setIndentSetting">
                        <i class="icon appfont app-indent"></i>
                        <span>缩进{{ editor.indentUnit }}字符</span>
                        <div class="indent-list" v-if="indentShow">
                            <div class="line"
                                 @click.stop="changeIndent(v)"
                                 v-for="(v, k) in indents"
                                 :class="{active:v === editor.indentUnit}"
                                 :key="k">
                                缩进{{ v }}字符
                            </div>
                        </div>
                    </div>
                    <div class="btn btn-skin" @click.stop="setThemeSetting()">
                        <i class="icon appfont app-skin"></i>
                        <span class="title" v-html="themeName(editor.theme)"></span>
                        <div class="skin-list" v-if="themeShow">
                            <div class="line"
                                 @click.stop="changeTheme(v)"
                                 v-for="(v, k) in themeList"
                                 :class="{active:v === editor.theme}"
                                 :key="k" v-html="themeName(v)">
                            </div>
                        </div>
                    </div>
                </div>
            </div>
            <div class="json">

                <!--输入区域-->
                <div class="content">
                    <!--输入框-->
                    <textarea @input="inputSource" v-model="jsonSource" placeholder="Enter the json"></textarea>

                    <!--拖动或点击上传-->
                    <label :for="uploadFileId" class="dragArea" :class="{dropActive:dropActive}" ref="dragArea">
                        <div class="icon appfont app-img-upload"></div>
                        <div class="title">拖动文件到此处，或点击上传</div>
                    </label>
                    <input :id="uploadFileId" type="file" :accept="accept" @change="getUploadFileContent">
                </div>

                <!--编辑器区域-->
                <div class="editor">
                    <div class="bar">
                        <tips class="tips" :tip="`点击复制`" :direction="'top'">
                            <i @click="copyTarget" class="copy appfont app-copy"></i>
                        </tips>
                        <tips class="tips" :tip="`保存为文件`" :direction="'top'">
                            <i @click="save" class="save appfont app-save"></i>
                        </tips>
                    </div>

                    <!--编辑器-->
                    <div class="box">
                        <b-ace-editor
                            ref="editor"
                            lang="json"
                            width="100%"
                            height="100%"
                            v-model="jsonTarget"
                            v-if="editor.init"
                            :theme="editor.theme"
                            :font-size="editor.fontSize"
                            :readonly="editor.readonly"
                            :wrap="editor.wrap"
                            :snippets="editor.snippets"
                            :options="editor.options"
                            @change="inputTarget"
                            @init="initTarget">
                        </b-ace-editor>
                    </div>
                </div>
            </div>
        </div>
    </div>
</template>

<script>
import Vue from 'vue';
import Editor from 'bin-ace-editor';
import { mapGetters } from 'vuex';
import kit from '../../electron/helper/kit';
import is from '../../electron/helper/is';
import IPC from '../../electron/ipc/dict';

/*@see https://wangbin3162.gitee.io/bin-ace-editor/#/guide*/
require('brace/mode/json');
require('brace/snippets/json');

Vue.component(Editor.name, Editor);

/*空json*/
const emptyJson = () => '{"tips":"Please enter JSON in the left input box"}';

export default {
    name: 'kit_json',
    data () {
        return {
            jsonSource: '',
            jsonTarget: '',

            /*编辑器*/
            editor: {
                init      : false,
                theme     : 'monokai',
                fontSize  : 14,
                wrap      : true,
                snippets  : true,
                readonly  : false,
                indentUnit: 4,
                options   : {
                    selectionStyle           : 'line',
                    highlightActiveLine      : true,
                    highlightSelectedWord    : true,
                    behavioursEnabled        : true,
                    wrapBehavioursEnabled    : true,
                    autoScrollEditorIntoView : true,
                    copyWithEmptySelection   : true,
                    useSoftTabs              : false,
                    showInvisibles           : true,
                    navigateWithinSoftTabs   : false,
                    enableMultiselect        : true,
                    enableBasicAutocompletion: true,
                    enableLiveAutocompletion : true,
                    animatedScroll           : true,
                    fontFamily               : 'Consolas, Monaco, "Andale Mono", "Ubuntu Mono", "微软雅黑"',
                    cursorStyle              : 'ace'
                }
            },

            /*皮肤列表*/
            themeList: [
                'monokai',
                'ambiance',
                'chaos',
                'chrome',
                'clouds',
                'clouds_midnight',
                'cobalt',
                'crimson_editor',
                'dawn',
                'dracula',
                'dreamweaver',
                'eclipse',
                'github',
                'gob',
                'gruvbox',
                'idle_fingers',
                'iplastic',
                'katzenmilch',
                'kr_theme',
                'kuroir',
                'merbivore',
                'merbivore_soft',
                'mono_industrial',
                'pastel_on_dark',
                'solarized_dark',
                'solarized_light',
                'sqlserver',
                'terminal',
                'textmate',
                'tomorrow',
                'tomorrow_night',
                'tomorrow_night_blue',
                'tomorrow_night_bright',
                'tomorrow_night_eighties',
                'twilight',
                'vibrant_ink',
                'xcode'
            ],

            /*缩进*/
            indents: [2, 4, 8],

            /*手动设置了只读*/
            userReadonly: false,

            /*源json是否为空*/
            isEmptyJson: false,

            /*indent设置层*/
            indentShow: false,

            /*theme设置层*/
            themeShow: false,

            /*拖动、上传管理*/
            dropActive  : false,
            uploadFileId: 'uploadFileId', /*文件上传域的id*/
            maxSize     : 10, /*单位M，最大上传尺寸*/
            accept      : '*'
        };
    },
    mounted () {
        this.init();

        /*利用拖动获取文件内容*/
        this.getDropFileContent();
    },
    computed: {
        themeName () {
            return theme => kit.toHump(theme, true);
        },
        ...mapGetters({
            'appSetting': 'setting/setting'
        })
    },
    watch   : {
        /*监控app设置更新*/
        appSetting: {
            deep: true,
            handler ({ kitJson }) {
                this.applySetting(kitJson);
            }
        }
    },
    methods : {
        /*初始化*/
        init () {
            /*设置默认皮肤*/
            this.loadTheme(this.editor.theme);

            /*应用配置*/
            this.applySetting(this.appSetting.kitJson);

            /*初始化*/
            this.isEmptyJson = true;
            this.jsonTarget  = emptyJson();
            this.format();
        },

        /*加载皮肤*/
        loadTheme (name) {
            require(`brace/theme/${name}`);
            this.editor.init = true;
        },

        /*应用配置*/
        applySetting (setting) {
            if (setting) {
                if (is.str(setting.theme)) this.editor.theme = setting.theme;
                if (is.num(setting.indentUnit)) this.editor.indentUnit = setting.indentUnit;
                this.loadTheme(this.editor.theme);
            }
        },

        /*压缩*/
        compress () {
            this.jsonSource = is.json(this.jsonSource) ? JSON.stringify(JSON.parse(this.jsonSource)) : this.jsonSource;
            this.jsonTarget = this.targetJson();
        },

        /*格式化*/
        format () {
            this.jsonSource = is.json(this.jsonSource)
                ? JSON.stringify(JSON.parse(this.jsonSource), null, this.editor.indentUnit)
                : this.jsonSource;

            const json      = this.targetJson();
            this.jsonTarget = is.json(json)
                ? JSON.stringify(JSON.parse(json), null, this.editor.indentUnit)
                : json;
        },

        /*目标json*/
        targetJson () {
            /*有源数据时*/
            if (this.jsonSource) {
                /*用户未设置过只读就取消只读*/
                if (!this.userReadonly) this.setReadonly(false);
                this.isEmptyJson = false;
                return this.jsonSource;
            }

            /*无源数据时强制只读*/
            this.setReadonly(true);
            this.isEmptyJson = true;
            return emptyJson();
        },

        /*源输入*/
        inputSource () {
            this.format();
        },

        /*目标初始化*/
        initTarget () {},

        /*目标编辑*/
        inputTarget (val) {
            this.jsonSource = this.isEmptyJson ? '' : val;
        },

        /*清理*/
        clear () {
            this.jsonSource = '';
            this.inputSource();
        },

        /*复制结果*/
        copyTarget () {
            this.$copy(this.jsonTarget);
        },

        /*保存文件*/
        save () {
            ipcRenderer.send(IPC.IPC_DIALOG.SAVE_FILE, { filter: 'Json File', data: this.jsonTarget });
            ipcRenderer.once(IPC.IPC_DIALOG.SAVE_FILE_RESULT, (e, data) => console.log(data));
        },

        /*设置只读*/
        setReadonly (value) {
            /*用户点击设置*/
            if (!is.bool(value)) {
                this.editor.readonly = !this.editor.readonly;
                this.userReadonly    = this.editor.readonly;
                return;
            }

            /*自动设置*/
            this.editor.readonly = value;
        },

        /*缩进设置*/
        changeIndent (indent) {
            this.editor.indentUnit = indent;
            this.format();
            this.updateSetting();
            this.setIndentSetting(false);
        },

        /*切换皮肤*/
        changeTheme (theme) {
            this.loadTheme(theme);
            this.editor.theme = theme;
            this.updateSetting();
            this.setThemeSetting(false);
        },

        /*切换皮肤设置*/
        setThemeSetting (set) {
            if (this.themeShow || set === false) {
                this.themeShow               = false;
                this.$refs.btns.style.zIndex = 1;
            }
            else {
                this.themeShow               = true;
                this.$refs.btns.style.zIndex = 1000;
            }
            this.indentShow = false;
        },

        /*切换缩进设置*/
        setIndentSetting (set) {
            if (this.indentShow || set === false) {
                this.indentShow              = false;
                this.$refs.btns.style.zIndex = 1;
            }
            else {
                this.indentShow              = true;
                this.$refs.btns.style.zIndex = 1000;
            }
            this.themeShow = false;
        },

        /*保存配置*/
        updateSetting () {
            this.$settingManager.update({
                kitJson: {
                    indentUnit: this.editor.indentUnit,
                    theme     : this.editor.theme
                }
            });
        },

        /**
         * 利用上传获取文件base64
         */
        getUploadFileContent (e) {
            /*文件转base64*/
            this.file2content(e.target.files);

            /*重置上传域*/
            let file   = document.getElementById(this.uploadFileId);
            file.value = '';
        },

        /**
         * 利用拖动获取文件base64
         */
        getDropFileContent () {
            /*监听拖动*/
            const dragArea = this.$refs.dragArea;

            /*公共处理*/
            const eHandler = (e, dropActive) => {
                this.dropActive = dropActive;
                e.stopPropagation();
                e.preventDefault();
            };

            dragArea.addEventListener('drop', (e) => {
                eHandler(e, false);
                this.file2content(e.dataTransfer.files); /*获取文件内容*/
            }, false);
            dragArea.addEventListener('dragleave', e => eHandler(e, false));
            dragArea.addEventListener('dragenter', e => eHandler(e, true));
            dragArea.addEventListener('dragover', e => eHandler(e, true));
        },

        /*文件对象转base64*/
        file2content (files) {
            try {
                if (files.length <= 0) return;

                if (files.length > 1) throw new Error('仅支持上传1个文件');

                if (files[0].size > (this.maxSize * 1024 * 1024)) throw new Error(`文件尺寸不能超过 ${this.maxSize}M`);

                /*记录已选文件*/
                let reader = new FileReader();
                reader.readAsText(files[0], 'utf8');
                reader.onload = () => {
                    this.jsonSource = reader.result;
                    this.inputSource();
                };
            }
            catch (e) {
                this.$alert(e.message);
            }
        }
    }
};
</script>


<style scoped lang="less">
/*复写编辑器样式*/
/deep/ .bin-ace-editor {
    -webkit-border-radius : 5px !important;
    -moz-border-radius    : 5px !important;
    border-radius         : 5px !important;
    border                : 0 !important;

    &.ace_dark {
        border : 1px solid #666 !important;
    }
}

@dragAreaHeight : 175px;
.btns {
    height        : 35px;
    margin-bottom : 10px;
    display       : flex;
    width         : 100%;
    align-items   : center;
    position      : relative;
    z-index       : 100;
    .noSelect;

    .icon {
        font-size    : 14px;
        margin-right : 5px;
    }

    .content {
        flex         : 1;
        margin-right : 20px;
        height       : 100%;
        display      : flex;
        align-items  : center;
    }

    .editor {
        flex        : 1;
        height      : 100%;
        display     : flex;
        align-items : center;
    }

    .btn {
        flex                  : auto;
        margin-right          : 10px;
        -webkit-border-radius : 5px;
        -moz-border-radius    : 5px;
        border-radius         : 5px;
        background            : rgba(255, 255, 255, 0.1);
        color                 : rgba(255, 255, 255, 0.8);
        height                : 35px;
        display               : flex;
        align-items           : center;
        justify-content       : center;
        padding               : 0 8px;
        cursor                : pointer;

        &:hover {
            color      : rgba(255, 255, 255, 1);
            background : fade(@green, 20%);
        }

        &.active, &.active:hover {
            background : fade(@green, 20%);
        }

        &:last-child {
            margin-right : 0;
        }

        @media screen and (max-width : 1200px) {
            &.btn-skin {
                width : 110px;
            }
        }

        &.btn-skin, &.btn-indent {
            position   : relative;
            z-index    : 1;
            background : fade(@green, 20%);

            &:hover {
                z-index : 9;
            }

            .title {
                .ellipsis-text-mul(1);
            }

            .skin-list, .indent-list {
                display               : block;
                position              : absolute;
                right                 : 0;
                top                   : 35px;
                z-index               : 2;
                width                 : 100%;
                border-top            : 1px solid #111;
                border-left           : 1px solid #111;
                background            : #111;
                -webkit-border-radius : 3px;
                -moz-border-radius    : 3px;
                border-radius         : 3px;
                overflow              : hidden;

                .line {
                    .noSelect;
                    background            : #333;
                    height                : 30px;
                    margin-top            : 0;
                    margin-right          : 0;
                    display               : flex;
                    align-items           : center;
                    justify-content       : center;
                    border-bottom         : 1px solid #111;
                    border-right          : 1px solid #111;
                    -webkit-border-radius : 3px;
                    -moz-border-radius    : 3px;
                    border-radius         : 3px;
                    font-size             : 12px;

                    &:hover {
                        background : #000;
                    }

                    &.active, &.active:hover {
                        background : @green-dark;
                    }
                }
            }

            .skin-list {
                display   : flex;
                width     : 541px;
                flex-wrap : wrap;

                .line {
                    width : 180px;
                }
            }
        }
    }
}

.json {
    display         : flex;
    align-items     : center;
    justify-content : center;
    height          : calc(~"100vh - 125px - 45px");

    .content {
        flex         : 1;
        margin-right : 20px;
        height       : 100%;
        position     : relative;
        z-index      : 200;

        textarea {
            width                 : 100%;
            height                : calc(~"100vh - 125px - 45px");
            border                : 0;
            -webkit-border-radius : 5px;
            -moz-border-radius    : 5px;
            border-radius         : 5px;
            padding               : 15px;
            font-size             : 16px;
            position              : relative;
            z-index               : 1;
            padding-bottom        : @dragAreaHeight + 30px;
        }

        .dragArea {
            cursor                : pointer;
            position              : absolute;
            z-index               : 9;
            background            : rgba(238, 238, 238, 0.9);
            border                : 2px dashed #aaa;
            color                 : #000;
            height                : @dragAreaHeight;
            -webkit-border-radius : 6px;
            -moz-border-radius    : 6px;
            border-radius         : 6px;
            bottom                : 15px;
            left                  : 15px;
            width                 : calc(~"100% - 30px");
            display               : flex;
            justify-content       : center;
            align-items           : center;
            flex-direction        : column;

            &.dropActive {
                background : fade(@blue, 80%);
                border     : 2px dashed @blue;

                .icon {
                    color : #fff;
                }

                .title {
                    color : #fff;
                }
            }

            .icon {
                font-size : 50px;
                color     : #999;
            }

            .title {
                margin-top : 10px;
                color      : #999;
            }
        }

        [type=file] {
            display : none;
        }
    }

    .editor {
        width    : 100%;
        flex     : 1;
        height   : calc(~"100vh - 125px - 45px");
        position : relative;
        z-index  : 300;

        .box {
            width    : 100%;
            height   : 100%;
            position : relative;
            z-index  : 1;
        }

        .bar {
            position        : absolute;
            z-index         : 100;
            right           : 10px;
            top             : 10px;
            display         : flex;
            align-items     : center;
            justify-content : flex-end;

            .tips {
                display         : flex;
                align-items     : center;
                justify-content : center;
                margin-left     : 5px;

                .copy, .save {
                    height                : 35px;
                    width                 : 35px;
                    display               : flex;
                    align-items           : center;
                    justify-content       : center;
                    cursor                : pointer;
                    color                 : #fff;
                    background            : rgba(0, 0, 0, 0.8);
                    -webkit-border-radius : 5px;
                    -moz-border-radius    : 5px;
                    border-radius         : 5px;

                    &:hover {
                        background : fade(@blue, 80%);
                        color      : #fff;
                    }
                }
            }
        }
    }
}
</style>
